// /**
// * @File:ble_rcv_files.c
// * @Version:V1.0
// * @Author:rudius
// * @Brief: File reception and storage via BLE or CLI
// * @Logs:
// * 	Date		Author		Notes
// * 	2025-07-05	rudius		first version
// */

// // <<< Use Configuration Wizard in Context Menu >>>

// /* Private includes --------------------------------------------------------- */
// #include "ble_rcv_files.h"
// #include "mx01.h"
// #include "ff.h"
// #include <string.h>
// #include <rtthread.h>

// #ifdef RT_USING_FINSH
// #include <finsh.h>
// #endif

// #ifdef __cplusplus
// extern "C" {
// #endif

// /* Private config ----------------------------------------------------------- */
// #define FILE_RCV_THREAD_STACK_SIZE 2048
// #define FILE_RCV_THREAD_PRIORITY   12
// #define FILE_RCV_BUFFER_SIZE       1024  // 1KB buffer for file chunks
// #define MAX_PATH_LEN               128   // Max path length
// #define TEMP_FILE_NAME             "_tempfile.tmp"

// /* Private defines ---------------------------------------------------------- */
// /* Private typedef ---------------------------------------------------------- */
// typedef struct {
//     char filename[FF_LFN_BUF];   // File name
//     uint32_t file_size;          // File size
//     uint32_t bytes_received;     // Bytes received so far
//     file_source_t source;        // File source
//     file_storage_t storage;      // Storage destination
//     char base_path[MAX_PATH_LEN];// Base storage path
// } file_receive_context_t;

// typedef struct {
//     file_receive_context_t ctx;
//     rt_thread_t worker_thread;
//     rt_mutex_t lock;
//     FIL file_handle;
//     FATFS fs;
//     uint8_t buffer[FILE_RCV_BUFFER_SIZE];
//     struct rym_ctx rym_ctx;
//     rt_device_t rym_device;
//     struct mx01_device *ble_dev;
// } file_rcv_module_t;

// /* Private variables -------------------------------------------------------- */
// static file_rcv_module_t file_rcv_mod;

// /* Private function prototypes ---------------------------------------------- */
// static void file_rcv_worker_thread_entry(void *parameter);
// static void file_rcv_reset_context(void);
// static void file_rcv_finish_transfer(void);
// static void file_rcv_rename_file(const char *old_path, const char *new_path);
// static rt_bool_t file_rcv_check_overwrite(const char *path);
// static enum rym_code file_rcv_ymodem_callback(struct rym_ctx *ctx,
//                                               rt_uint8_t *buf,
//                                               rt_size_t len);
// static void file_rcv_cli_prompt_user(void);

// /*----------------------------------------------------------------------------*/
// /* Private functions -------------------------------------------------------- */

// /**
//  * @brief Initialize file receive module
//  * @return RT_EOK on success, error code otherwise
//  */
// int file_rcv_init(void)
// {
//     // Initialize context
//     memset(&file_rcv_mod, 0, sizeof(file_rcv_module_t));
    
//     // Create mutex
//     file_rcv_mod.lock = rt_mutex_create("file_rcv_mtx", RT_IPC_FLAG_PRIO);
//     if (!file_rcv_mod.lock) {
//         return -RT_ENOMEM;
//     }
    
//     // Create worker thread
//     file_rcv_mod.worker_thread = rt_thread_create(
//         "file_rcv_worker",
//         file_rcv_worker_thread_entry,
//         RT_NULL,
//         FILE_RCV_THREAD_STACK_SIZE,
//         FILE_RCV_THREAD_PRIORITY,
//         10);
    
//     if (!file_rcv_mod.worker_thread) {
//         rt_mutex_delete(file_rcv_mod.lock);
//         return -RT_ENOMEM;
//     }
    
//     // Start worker thread
//     rt_thread_startup(file_rcv_mod.worker_thread);
    
//     return RT_EOK;
// }

// /**
//  * @brief File receive worker thread entry
//  */
// static void file_rcv_worker_thread_entry(void *parameter)
// {
//     while (1) {
//         // Check for completed transfers
//         rt_mutex_take(file_rcv_mod.lock, RT_WAITING_FOREVER);
        
//         if (file_rcv_mod.ctx.bytes_received >= file_rcv_mod.ctx.file_size && 
//             file_rcv_mod.ctx.file_size > 0) {
            
//             // Close the temp file
//             f_close(&file_rcv_mod.file_handle);
            
//             // Process the file (rename and overwrite handling)
//             file_rcv_finish_transfer();
            
//             // Reset context for next transfer
//             file_rcv_reset_context();
//         }
        
//         rt_mutex_release(file_rcv_mod.lock);
        
//         // Sleep to reduce CPU usage
//         rt_thread_mdelay(100);
//     }
// }

// /**
//  * @brief Reset receive context
//  */
// static void file_rcv_reset_context(void)
// {
//     memset(&file_rcv_mod.ctx, 0, sizeof(file_receive_context_t));
//     file_rcv_mod.ctx.storage = FILE_STORAGE_NONE;
// }

// /**
//  * @brief Finish file transfer process
//  */
// static void file_rcv_finish_transfer(void)
// {
//     char temp_path[MAX_PATH_LEN];
//     char final_path[MAX_PATH_LEN];
    
//     // Build temporary file path
//     rt_snprintf(temp_path, MAX_PATH_LEN, "%s/%s", 
//              file_rcv_mod.ctx.base_path, TEMP_FILE_NAME);
    
//     // Build final file path
//     rt_snprintf(final_path, MAX_PATH_LEN, "%s/%s",
//                file_rcv_mod.ctx.base_path, file_rcv_mod.ctx.filename);
    
//     // Check for file overwrite conflicts
//     if (file_rcv_check_overwrite(final_path)) {
//         // User confirmed overwrite - proceed
//         file_rcv_rename_file(temp_path, final_path);
//         rt_kprintf("File saved: %s\n", final_path);
//     } else {
//         // User canceled overwrite - delete temp file
//         f_unlink(temp_path);
//         rt_kprintf("Transfer canceled\n");
//     }
// }

// /**
//  * @brief Rename temp file to final name
//  * @param old_path Temporary file path
//  * @param new_path Final file path
//  */
// static void file_rcv_rename_file(const char *old_path, const char *new_path)
// {
//     FRESULT res;
    
//     // Delete existing file if it exists
//     f_unlink(new_path);
    
//     // Rename temp file to final name
//     res = f_rename(old_path, new_path);
//     if (res != FR_OK) {
//         rt_kprintf("Rename error: %d\n", res);
//     }
// }

// /**
//  * @brief Check if file exists and prompt for overwrite
//  * @param path File path to check
//  * @return RT_TRUE to overwrite, RT_FALSE to cancel
//  */
// static rt_bool_t file_rcv_check_overwrite(const char *path)
// {
//     FILINFO fno;
//     FRESULT res;
    
// #ifdef RT_USING_FINSH
//     char cmd[16] = {0};
// #endif

//     // Check if file exists
//     res = f_stat(path, &fno);
//     if (res != FR_OK || (fno.fattrib & AM_DIR)) {
//         // File doesn't exist or is a directory - no overwrite conflict
//         return RT_TRUE;
//     }
    
//     rt_kprintf("File %s already exists.\n", file_rcv_mod.ctx.filename);
//     rt_kprintf("Overwrite? [y/N] > ");
    
// #ifdef RT_USING_FINSH
//     // 使用RT-Thread的标准控制台输入API
//     if (rt_device_read(rt_console_get_device(), 0, cmd, sizeof(cmd) - 1) > 0) {
//         // 查找换行符
//         char *newline = strchr(cmd, '\r');
//         if (!newline) newline = strchr(cmd, '\n');
//         if (newline) *newline = '\0';
        
//         // 输出用户选择用于调试
//         rt_kprintf("User input: %s\n", cmd);
        
//         // 检查用户响应
//         if (cmd[0] == 'y' || cmd[0] == 'Y') {
//             return RT_TRUE;
//         }
//     }
//     return RT_FALSE;
// #else
//     // 对于非控制台源，默认覆盖
//     return RT_TRUE;
// #endif
// }

// /**
//  * @brief Start file reception
//  * @param source File source (BLE/CLI/Console)
//  * @param storage Destination storage
//  * @param base_path Base storage path (e.g. "0:/" for SD, "1:/" for Flash)
//  * @return RT_EOK on success, error code otherwise
//  */
// int file_rcv_start(file_source_t source, file_storage_t storage, const char *base_path)
// {
//     FRESULT fr;
//     char temp_path[MAX_PATH_LEN];
    
//     // Validate parameters
//     if (storage == FILE_STORAGE_NONE || base_path == NULL || strlen(base_path) == 0) {
//         return -RT_EINVAL;
//     }
    
//     rt_mutex_take(file_rcv_mod.lock, RT_WAITING_FOREVER);
    
//     // Check if module is busy
//     if (file_rcv_mod.ctx.storage != FILE_STORAGE_NONE) {
//         rt_mutex_release(file_rcv_mod.lock);
//         return -RT_EBUSY;
//     }
    
//     // Mount filesystem
//     fr = f_mount(&file_rcv_mod.fs, base_path, 1);
//     if (fr != FR_OK) {
//         rt_mutex_release(file_rcv_mod.lock);
//         rt_kprintf("Mount failed: %d\n", fr);
//         return -RT_ERROR;
//     }
    
//     // Build temp file path
//     rt_snprintf(temp_path, MAX_PATH_LEN, "%s/%s", base_path, TEMP_FILE_NAME);
    
//     // Open temp file
//     fr = f_open(&file_rcv_mod.file_handle, temp_path, FA_CREATE_ALWAYS | FA_WRITE);
//     if (fr != FR_OK) {
//         f_unmount(base_path);
//         rt_mutex_release(file_rcv_mod.lock);
//         rt_kprintf("Temp file creation failed: %d\n", fr);
//         return -RT_ERROR;
//     }
    
//     // Update context
//     file_rcv_mod.ctx.source = source;
//     file_rcv_mod.ctx.storage = storage;
//     strncpy(file_rcv_mod.ctx.base_path, base_path, MAX_PATH_LEN - 1);
//     file_rcv_mod.ctx.bytes_received = 0;
    
//     // Select device based on source
//     switch (source) {
//     case FILE_SOURCE_BLE:
//         if (file_rcv_mod.ble_dev && file_rcv_mod.ble_dev->uart_dev) {
//             file_rcv_mod.rym_device = file_rcv_mod.ble_dev->uart_dev;
//         } else {
//             f_close(&file_rcv_mod.file_handle);
//             f_unmount(base_path);
//             rt_mutex_release(file_rcv_mod.lock);
//             return -RT_ERROR;
//         }
//         break;
        
//     case FILE_SOURCE_CLI:
//     case FILE_SOURCE_CONSOLE:
//         file_rcv_mod.rym_device = rt_console_get_device();
//         break;
        
//     default:
//         f_close(&file_rcv_mod.file_handle);
//         f_unmount(base_path);
//         rt_mutex_release(file_rcv_mod.lock);
//         return -RT_EINVAL;
//     }
    
//     // Configure RYM
//     file_rcv_mod.rym_ctx.on_begin = RT_NULL;
//     file_rcv_mod.rym_ctx.on_data = file_rcv_ymodem_callback;
//     file_rcv_mod.rym_ctx.on_end = RT_NULL;
    
//     // Start RYM reception
//     rt_err_t result = rym_recv_on_device(&file_rcv_mod.rym_ctx,
//                                          file_rcv_mod.rym_device,
//                                          RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
//                                          RT_NULL,
//                                          file_rcv_ymodem_callback,
//                                          RT_NULL,
//                                          RT_WAITING_FOREVER);
    
//     if (result != RT_EOK) {
//         f_close(&file_rcv_mod.file_handle);
//         f_unmount(file_rcv_mod.ctx.base_path);
//         file_rcv_reset_context();
//         rt_mutex_release(file_rcv_mod.lock);
//         rt_kprintf("RYM init failed: %d\n", result);
//         return -RT_ERROR;
//     }
    
//     rt_mutex_release(file_rcv_mod.lock);
//     return RT_EOK;
// }

// /**
//  * @brief RYM callback for file reception
//  */
// static enum rym_code file_rcv_ymodem_callback(struct rym_ctx *ctx,
//                                               rt_uint8_t *buf,
//                                               rt_size_t len)
// {
//     UINT bytes_written;
//     FRESULT fr;
//     enum rym_code ret_code = RYM_CODE_ACK;  // 默认返回ACK

//     rt_mutex_take(file_rcv_mod.lock, RT_WAITING_FOREVER);
    
//     // Parse filename from first packet (C-style string)
//     if (file_rcv_mod.ctx.file_size == 0 && buf[0] != 0) {
//         char *p = (char *)buf;
//         char *end = p + len;
//         while (*p && p < end) {
//             file_rcv_mod.ctx.filename[p - (char *)buf] = *p;
//             p++;
//         }
//         file_rcv_mod.ctx.filename[p - (char *)buf] = '\0';
//         rt_kprintf("Receiving file: %s\n", file_rcv_mod.ctx.filename);
//     } else {
//         // Write data to file
//         fr = f_write(&file_rcv_mod.file_handle, buf, len, &bytes_written);
//         if (fr != FR_OK || bytes_written != len) {
//             rt_kprintf("Write error: %d\n", fr);
//             ret_code = RYM_CODE_CAN;  // 发生错误返回CAN
//             goto exit;
//         }
        
//         // Update receive stats
//         file_rcv_mod.ctx.bytes_received += len;
//         file_rcv_mod.ctx.file_size += len;
//     }

// exit:
//     rt_mutex_release(file_rcv_mod.lock);
//     return ret_code;
// }

// /**
//  * @brief BLE data callback for file reception
//  */
// void file_rcv_ble_callback(struct mx01_device *dev,
//                           const uint8_t *buffer,
//                           rt_size_t size,
//                           void *context)
// {
//     // Only process BLE data if we're in BLE mode
//     if (file_rcv_mod.ctx.source != FILE_SOURCE_BLE) return;
    
//     // Pass data to Ymodem handler
//     file_rcv_ymodem_callback(&file_rcv_mod.rym_ctx, (rt_uint8_t *)buffer, size);
// }

// /********************** CLI Command Interface **********************/
// #ifdef RT_USING_FINSH

// /**
//  * @brief CLI command: Start file transfer via Bluetooth
//  */
// void file_rcv_start_ble(int storage_type, const char *path)
// {
//     file_storage_t storage = (storage_type == 0) ? 
//         FILE_STORAGE_FLASH : FILE_STORAGE_SD;
    
//     int result = file_rcv_start(FILE_SOURCE_BLE, storage, path);
//     if (result != RT_EOK) {
//         rt_kprintf("Start failed: %d\n", result);
//     }
// }
// MSH_CMD_EXPORT(file_rcv_start_ble, Start BLE file transfer: file_rcv_start_ble <0=Flash/1=SD> <path>);

// /**
//  * @brief CLI command: Start file transfer via CLI
//  */
// void file_rcv_start_cli(int storage_type, const char *path)
// {
//     file_storage_t storage = (storage_type == 0) ? 
//         FILE_STORAGE_FLASH : FILE_STORAGE_SD;
    
//     int result = file_rcv_start(FILE_SOURCE_CLI, storage, path);
//     if (result != RT_EOK) {
//         rt_kprintf("Start failed: %d\n", result);
//     }
// }
// MSH_CMD_EXPORT(file_rcv_start_cli, Start CLI file transfer: file_rcv_start_cli <0=Flash/1=SD> <path>);

// /**
//  * @brief CLI command: Get file transfer status
//  */
// void file_rcv_status(void)
// {
//     rt_mutex_take(file_rcv_mod.lock, RT_WAITING_FOREVER);
    
//     rt_kprintf("File: %s\n", file_rcv_mod.ctx.filename);
//     rt_kprintf("Status: %s\n", 
//         file_rcv_mod.ctx.storage == FILE_STORAGE_NONE ? "Idle" : "Active");
        
//     if (file_rcv_mod.ctx.storage != FILE_STORAGE_NONE) {
//         uint32_t progress = (file_rcv_mod.ctx.file_size > 0) ? 
//             (file_rcv_mod.ctx.bytes_received * 100) / file_rcv_mod.ctx.file_size : 0;
//         rt_kprintf("Progress: %d%% (%d/%d bytes)\n", 
//                   progress,
//                   file_rcv_mod.ctx.bytes_received,
//                   file_rcv_mod.ctx.file_size);
//         rt_kprintf("Destination: %s\n", file_rcv_mod.ctx.base_path);
//     }
    
//     rt_mutex_release(file_rcv_mod.lock);
// }
// MSH_CMD_EXPORT(file_rcv_status, Show file transfer status);

// #endif /* RT_USING_FINSH */

// /* Public functions --------------------------------------------------------- */

// #ifdef __cplusplus
// }
// #endif
// // <<< end of configuration section >>>
// /* ----------------------------- end of ble_rcv_files.c ---------------------------- */